/*
 * Jamus
 * Copyright (C) 2010 Jamus Team
 *
 * Jamus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jamus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jamus.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * QFunctionAdaptor.cpp
 *
 *  Created on: Feb 3, 2010
 *      Author: christian moellinger
 */

#include "QPlayerAdaptor.h"
#include "QFunctionWrapper.h"
#include "../../Track.h"

std::vector<Track> tracklist;




// Marshall the DBusStatus data into a D-BUS argument
QDBusArgument &operator<<(QDBusArgument &argument, const DBusStatus &status)
{
    argument.beginStructure();
    argument << status.Play;
    argument << status.Random;
    argument << status.Repeat;
    argument << status.RepeatPlaylist;
    argument.endStructure();
    return argument;
}

// Retrieve the DBusStatus data from the D-BUS argument
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusStatus &status)
{
    argument.beginStructure();
    argument >> status.Play;
    argument >> status.Random;
    argument >> status.Repeat;
    argument >> status.RepeatPlaylist;
    argument.endStructure();
    return argument;
}

QPlayerAdaptor::QPlayerAdaptor(QObject *parent) :
			QDBusAbstractAdaptor(parent),
			parent(parent),
			logger(log4cxx::Logger::getLogger(
					"frontend.amarok.Plugin.PlayerAdaptor")),
			backend(
					dynamic_cast<QFunctionWrapper *> (this->parent)->backendPlugin)
{
	backend.connectPlaybackStatusChanged(
			backend::Plugin::PlaybackStatusChangedSlot(
					&QPlayerAdaptor::onPlaybackStatusChanged, this));
    qDBusRegisterMetaType<DBusStatus>();

    playState=2;
    newState=this->GetCaps();
	oldTrack=-1;
	try {
	currentTrack = backend.getCurrentTrack();
	}
	catch (std::exception &e)
	{
		currentTrack = -1;
	}
}

void QPlayerAdaptor::onPlaybackStatusChanged()
{
	LOG4CXX_TRACE(logger,"backend::Plugin::onPlaybackStatusChanged() called");
	try
	{
		oldTrack = -1;
		currentTrack = backend.getCurrentTrack();
		LOG4CXX_DEBUG(logger,"current track: " << currentTrack << "|" << oldTrack);
		newState = this->GetCaps();
	} catch (std::exception &e)
	{
		LOG4CXX_ERROR(logger,"frontend::Plugin::onPlaybackStatusChanged() catched exception");
	}
}

void QPlayerAdaptor::emitSignals()
{
	static bool firstrun=1;
	boost::lock_guard<boost::mutex> lock(mutex);

	if (firstrun==1) {
		emit StatusChange(this->GetStatus());
		emit CapsChange(newState);
		firstrun=false;
	}
	else
	if (newState != 0)
	{
		LOG4CXX_DEBUG(logger,"signal emitted");

		emit StatusChange(this->GetStatus());
		emit CapsChange(newState);

		if (oldTrack!=currentTrack) {
			emit TrackChange(this->GetMetadata());
			oldTrack=currentTrack;
		}
		newState = 0;
	}


}
void QPlayerAdaptor::Backward(int milliseconds)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	int newPosition = (backend.getElapsed().total_seconds() - milliseconds/1000);
	if (newPosition < 0)
		newPosition = 0;

	backend.seek(boost::posix_time::seconds(newPosition));
	}
	catch (std::exception &e)
	{

	}
}

void QPlayerAdaptor::Forward(int milliseconds)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	int newPosition = (backend.getElapsed().total_seconds() + milliseconds/1000);

	/* todo: check that newPosition <= tracklength */

	backend.seek(boost::posix_time::seconds(newPosition));
	}
	catch (std::exception &e)
	{
	}
}


DBusStatus QPlayerAdaptor::GetStatus()
   {
       DBusStatus status = { 0, 0, 0, 0 };
       status.Play = playState;
       status.Random = 0;
       status.Repeat = 0;
       status.RepeatPlaylist = 0;
       return status;
   }

int QPlayerAdaptor::GetCaps()
{
	int currentTrack;
	try {
		tracklist = backend.getPlaylist();
		//oldTrack = currentTrack;
		currentTrack = backend.getCurrentTrack();
	}
	catch (std::exception &e)
	{
		currentTrack = 0;
	}

	boost::lock_guard<boost::mutex> lock(mutex);

	backend::Plugin::PlaybackStatus state = backend.getPlaybackStatus();

	bool can_go_next = ((unsigned int)(currentTrack+1) < tracklist.size()) ? true : false;
	bool can_go_prev = (currentTrack > 0);
	bool can_pause;
	bool can_play;
	bool can_seek;
	bool can_provide_metadata;
	bool can_has_tracklist = (tracklist.size()>0);

	if (state == backend::Plugin::STOP)
		{
		can_play=true;
		can_pause=false;
		can_seek=false;
		can_provide_metadata=false;
		playState=2;
		}
	else if (state == backend::Plugin::PLAY)
		{
		can_pause=true;
		can_play=false;
		can_seek=true;
		can_provide_metadata=true;
		playState=0;
		}
	else if (state == backend::Plugin::PAUSE)
	{
		can_play=true;
		can_pause=false;
		can_seek=true;
		can_provide_metadata=true;
		playState=1;
	}
	else
		LOG4CXX_ERROR(logger,"backend::Plugin::getPlaybackStatus() returns unknown value");

	int returnValue = 0;
	if (can_go_next)
		returnValue += 1;
	if (can_go_prev)
		returnValue += 2;
	if (can_pause)
		returnValue += 4;
	if (can_play)
		returnValue += 8;
	if (can_seek)
		returnValue += 16;
	if (can_provide_metadata)
		returnValue += 32;
	if (can_has_tracklist)
		returnValue += 64;

	return returnValue;
}

QVariantMap QPlayerAdaptor::GetMetadata()
{

	try {
	tracklist = backend.getPlaylist();
	int currentId = backend.getCurrentTrack();
	Track currentTrack = tracklist.at(currentId);

	QVariantMap currentTrackMapped;
	currentTrackMapped.insert("artist", QString(
			currentTrack.getArtist().c_str()));
	currentTrackMapped.insert("title", QString(currentTrack.getTitle().c_str()));
	currentTrackMapped.insert("album", QString(currentTrack.getAlbum().c_str()));
	currentTrackMapped.insert("location",QString(currentTrack.getFileName().c_str()));
	currentTrackMapped.insert("time",currentTrack.getDuration().total_seconds());
	currentTrackMapped.insert("mtime",1000*currentTrack.getDuration().total_seconds());
	currentTrackMapped.insert("tracknumber",currentTrack.getTrackNumber());
	return currentTrackMapped;
	}
	catch (std::exception &e)
	{
		return QVariantMap();
	}
}

void QPlayerAdaptor::LoadThemeFile(QString name)
{
	LOG4CXX_DEBUG(logger,"(void) LoadThemeFile() not implemented");
}

void QPlayerAdaptor::Mute()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	backend.setVolume(0); //workaround
	}
	catch (std::exception *e)
	{

	}
}

void QPlayerAdaptor::Next()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	backend.nextTrack();
	}
	catch (std::exception &e)
	{

	}
}
void QPlayerAdaptor::Pause()
{
	/* the original amarok dbus interface (amarok 2.1)
	 * provides pause() and playpause(), but both calls do exactly the same (play if paused, pause if playing)
	 * so we should do the same, although that might be different to the mpris-specification
	 */

	this->PlayPause();

	/* boost::lock_guard<boost::mutex> lock(mutex);

	LOG4CXX_DEBUG(logger,"(PAUSE)");
	try {
		backend.pause();
	}
	catch (std::exception &e)
	{

	}
	*/
}
void QPlayerAdaptor::Play()
{
	boost::lock_guard<boost::mutex> lock(mutex);
	LOG4CXX_DEBUG(logger,"(PLAY)");
	try {
	backend.play();
	}
	catch (std::exception &e)
	{

	}
}
void QPlayerAdaptor::PlayPause()
{
	boost::lock_guard<boost::mutex> lock(mutex);
	LOG4CXX_DEBUG(logger,"(PLAYPAUSE)");
	try {
	backend.playPause();
	}
	catch (std::exception &e)
	{

	}
}

int QPlayerAdaptor::PositionGet()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	LOG4CXX_TRACE(logger,"backend.getElapsed() returns: " << backend.getElapsed().total_seconds());
	return backend.getElapsed().total_milliseconds();
	}
	catch (std::exception &e)
	{
		return 0;
	}
}

void QPlayerAdaptor::PositionSet(int milliseconds)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	backend.seek(boost::posix_time::seconds(milliseconds/1000));
	}
	catch (std::exception &e)
	{

	}
}

void QPlayerAdaptor::Prev()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
		backend.prevTrack();
	}
	catch (std::exception &e)
	{

	}
}

void QPlayerAdaptor::Repeat(bool on)
{
	LOG4CXX_ERROR(logger,"(void) Repeat(bool) not implemented");
}

void QPlayerAdaptor::ShowOSD()
{
	LOG4CXX_DEBUG(logger,"(void) ShowOSD() not implemented");
}

void QPlayerAdaptor::Stop()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	backend.stop();
	}
	catch (std::exception &e)
	{
	}
}

void QPlayerAdaptor::VolumeDown(int value)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
		int newVolume = backend.getVolume() - value;
		if (newVolume < 0)
			newVolume = 0;
		backend.setVolume(newVolume);
	}
	catch (std::exception &e)
	{

	}
}

int QPlayerAdaptor::VolumeGet()
{
	boost::lock_guard<boost::mutex> lock(mutex);

	return backend.getVolume();
}

void QPlayerAdaptor::VolumeSet(int value)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
	backend.setVolume(value);
	}
	catch (std::exception &e)
	{

	}
}
void QPlayerAdaptor::VolumeUp(int value)
{
	boost::lock_guard<boost::mutex> lock(mutex);

	try {
		int newVolume = backend.getVolume() + value;
		if (newVolume > 100)
			newVolume = 100;
		backend.setVolume(newVolume);
	}
	catch (std::exception &e)
	{

	}
}

